/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using DaveSexton.DocProject.Sandcastle;

namespace DaveSexton.DocProject.DeploymentSandcastle
{
	internal static class DeploymentContentFactory
	{
		public static IEnumerable<DeploymentContent> CreateContent(DeploymentContentTypes contentTypes, DeploymentContext context)
		{
			return new MarshalByRefEnumerable<DeploymentContent>(CreateContentInternal, contentTypes, context);
		}

		public static IEnumerable<DeploymentContent> CreateContentInternal(object[] arguments)
		{
			DeploymentContentTypes contentTypes = (DeploymentContentTypes) arguments[0];
			DeploymentContext context = (DeploymentContext) arguments[1];

			if (context == null)
				throw new ArgumentNullException("context");

			IDocProject project = context.Project;
			SandcastleSettings settings = context.Settings;
			DeploymentSandcastleProjectOptions options = context.Options;

			string projectDirectory = context.ProjectDirectory;
			string projectOutputDirectory = project.Output.OutputPath;
			string relativeProjectOutputDirectory = project.Output.RelativeOutputPath;
			string sourceDirectory = context.SourceDirectory;
			string rootTargetDirectory = context.RootTargetDirectory;
			string targetDirectory = context.TargetDirectory;

			if (context.DeployDocSiteContentOnly && project.IsDocSite && (contentTypes & DeploymentContentTypes.DocSiteOutput) == DeploymentContentTypes.DocSiteOutput)
			// DocSite files are generated after all build steps have been executed, so they must be deployed out-of-band.
			// Only set the DeployDocSiteContentOnly to true and execute this step after the DocSite files have been created.
			{
				yield return new DeploymentContent(settings.DocSiteDataPath, Path.Combine(rootTargetDirectory, settings.RelativeDocSiteDataPath), settings.DocSiteContentsXmlFileName);
				yield return new DeploymentContent(settings.DocSiteDataPath, Path.Combine(rootTargetDirectory, settings.RelativeDocSiteDataPath), settings.DocSiteIndexXmlFileName);
				yield break;
			}

			bool deployProjectOutput = (contentTypes & DeploymentContentTypes.ProjectOutput) == DeploymentContentTypes.ProjectOutput;

			bool produceHelp1x = options.Help1xProject.OutputEnabled;
			bool produceHelp2x = options.Help2xProject.OutputEnabled;

			if (produceHelp1x && (contentTypes & DeploymentContentTypes.Chm) == DeploymentContentTypes.Chm && (!deployProjectOutput || project.IsDocSite))
				yield return new DeploymentContent(settings.CompiledHelp1xFile, Path.Combine(rootTargetDirectory, settings.RelativeCompiledHelp1xFile));

			if (produceHelp2x && (contentTypes & DeploymentContentTypes.Hxs) == DeploymentContentTypes.Hxs && (!deployProjectOutput || project.IsDocSite))
				yield return new DeploymentContent(settings.CompiledHelp2xFile, Path.Combine(rootTargetDirectory, settings.RelativeCompiledHelp2xFile));

			if ((contentTypes & DeploymentContentTypes.HelpProject) == DeploymentContentTypes.HelpProject)
			{
				string documentationSetName = settings.DocumentationSetFileName;

				if (!context.PartialBuildItemsOnly && produceHelp1x)
				{
					yield return new DeploymentContent(sourceDirectory, targetDirectory, documentationSetName + ".hhp");
					yield return new DeploymentContent(sourceDirectory, targetDirectory, documentationSetName + ".hhc");
					yield return new DeploymentContent(sourceDirectory, targetDirectory, documentationSetName + ".hhk");
				}

				if (produceHelp2x)
				{
					if (!context.PartialBuildItemsOnly)
					{
						yield return new DeploymentContent(sourceDirectory, targetDirectory, documentationSetName + ".HxC");
						yield return new DeploymentContent(sourceDirectory, targetDirectory, documentationSetName + ".HxT");
					}

					yield return new DeploymentContent(sourceDirectory, targetDirectory, settings.Help2xFilesCollectionFileName);
					yield return new DeploymentContent(sourceDirectory, targetDirectory, settings.Help2xAssociativeIndexFileName);
					yield return new DeploymentContent(sourceDirectory, targetDirectory, settings.Help2xDynamicLinkIndexFileName);
					yield return new DeploymentContent(sourceDirectory, targetDirectory, settings.Help2xContextSensitiveIndexFileName);
					yield return new DeploymentContent(sourceDirectory, targetDirectory, settings.Help2xKeywordIndexFileName);
					yield return new DeploymentContent(sourceDirectory, targetDirectory, settings.Help2xNamedUrlIndexFileName);
					yield return new DeploymentContent(sourceDirectory, targetDirectory, settings.Help2xSearchWindowIndexFileName);
				}
			}

			if (!context.PartialBuildItemsOnly)
			{
				if (produceHelp1x && (contentTypes & DeploymentContentTypes.Html) == DeploymentContentTypes.Html)
					yield return new DeploymentContent(sourceDirectory, targetDirectory, @"Html\");

				if (produceHelp2x && (contentTypes & DeploymentContentTypes.Html2) == DeploymentContentTypes.Html2)
					yield return new DeploymentContent(sourceDirectory, targetDirectory, @"Html2\");

				if ((contentTypes & DeploymentContentTypes.Code) == DeploymentContentTypes.Code)
					foreach (DeploymentContent item in FindSourceCodeRecursive(project, projectDirectory, rootTargetDirectory))
						yield return item;

				if ((contentTypes & DeploymentContentTypes.AspNet) == DeploymentContentTypes.AspNet)
					foreach (DeploymentContent item in FindAspNetFilesRecursive(projectDirectory, rootTargetDirectory))
						yield return item;

				if (deployProjectOutput)
					foreach (DeploymentContent item in FindContentRecursive(projectOutputDirectory, Path.Combine(rootTargetDirectory, relativeProjectOutputDirectory)))
						yield return item;
			}
		}

		private static IEnumerable<DeploymentContent> FindContentRecursive(string directory, string rootTarget)
		{
			return FindContentRecursive(directory, rootTarget, null);
		}

		private static IEnumerable<DeploymentContent> FindContentRecursive(string directory, string rootTarget, string searchPattern)
		{
			Uri directoryUri = new Uri(directory);

			foreach (string file in (searchPattern != null)
				? Directory.GetFiles(directory, searchPattern, SearchOption.AllDirectories)
				: Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories))
			{
				Uri source = new Uri(file);

				// NOTE: If source has any spaces they will be escaped by MakeRelativeUri, so UrlDecode is required
				string relativeSource = System.Web.HttpUtility.UrlDecode(directoryUri.MakeRelativeUri(source).ToString());

				Uri target = new Uri(Path.Combine(rootTarget, relativeSource));

				yield return new DeploymentContent(source, target);
			}
		}

		private static IEnumerable<DeploymentContent> FindAspNetFilesRecursive(string directory, string rootTarget)
		{
			foreach (DeploymentContent content in FindContentRecursive(directory, rootTarget, "*.aspx"))
				yield return content;

			foreach (DeploymentContent content in FindContentRecursive(directory, rootTarget, "*.ascx"))
				yield return content;

			foreach (DeploymentContent content in FindContentRecursive(directory, rootTarget, "*.asmx"))
				yield return content;
		}

		private static IEnumerable<DeploymentContent> FindSourceCodeRecursive(IDocProject project, string directory, string rootTarget)
		{
			string pattern;

			if (project.IsVisualBasic)
				pattern = "*.vb";
			else if (project.IsCSharp)
				pattern = "*.cs";
			else if (project.IsJSharp)
				pattern = "*.js";
			else
				return null;

			return FindContentRecursive(directory, rootTarget, pattern);
		}
	}
}
